package com.neasho.neashoadminv1.web.service.generator

import com.fasterxml.jackson.databind.ObjectMapper
import com.neasho.neashoadminv1.infrastructure.domain.generator.AppTable
import com.neasho.neashoadminv1.infrastructure.domain.generator.AppTableColumn
import com.neasho.neashoadminv1.infrastructure.domain.generator.AppTableView
import com.neasho.neashoadminv1.infrastructure.domain.generator.tableName
import com.neasho.neashoadminv1.infrastructure.domain.generator.tableSchema
import com.neasho.neashoadminv1.infrastructure.template.renderSimpleTemplate
import com.neasho.neashoadminv1.web.console.generator.GeneratorRenderSpec
import jakarta.servlet.http.HttpServletResponse
import org.babyfish.jimmer.jackson.ImmutableModule
import org.babyfish.jimmer.sql.kt.KSqlClient
import org.babyfish.jimmer.sql.kt.ast.expression.eq
import org.babyfish.jimmer.sql.kt.ast.expression.valueIn
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.file.Files
import java.nio.file.Path
import java.time.LocalDateTime
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

@Service
class GeneratorService(
    private val sqlClient: KSqlClient,
) {

    @Value("\${spring.datasource.database-name}")
    val databaseName:String = ""

    val log: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(GeneratorService::class.java)

    fun queryTables(input: GeneratorRenderSpec): List<AppTable> {
        return sqlClient.createQuery(AppTable::class) {
            where(table.tableSchema eq databaseName)
            where(table.tableName valueIn input.tableNames)
            select(table)
        }.execute()
    }

    fun queryColumns(input: GeneratorRenderSpec): List<AppTableColumn> {
        return sqlClient.createQuery(AppTableColumn::class) {
            where(table.tableSchema eq databaseName)
            where(table.tableName valueIn input.tableNames)
            select(table)
        }.execute()
    }

    val mapper = ObjectMapper().also {
        it.registerModule(ImmutableModule())
    }
    fun renderTable(tableList: List<AppTableView>, response: HttpServletResponse) {
        // 申请一个临时目录
        val tempDir = Files.createTempDirectory("generator")
        tableList.forEach { view ->
            // controller.ftl
            renderFile("controller.ftl", "controller/${view.upperName}Controller.kt",tempDir, view)
            // dto.ftl
            renderFile("dto.ftl", "dto/${view.upperName}.dto",tempDir, view)
            // entity.ftl
            renderFile("entity.ftl", "domain/${view.packagePrefix}/${view.upperName}.kt",tempDir, view)
            // enum.ftl
            view.enumList.forEach {
                renderFile("enum.ftl", "domain/${view.packagePrefix}/${it.enumName}.kt", tempDir, it)
            }
            // service.ftl
            renderFile("service.ftl", "service/${view.packagePrefix}/${view.upperName}Service.kt",tempDir, view)
        }
        // 将tempDir目录达成一个压缩包response流写出
        val zipFilePath = "${tempDir.toFile().absolutePath}.zip"
        compressDirectoryToZip(tempDir.toFile().absolutePath, zipFilePath)
        // 将zipFilePath文件中的内容通过response写出去
        response.contentType = "application/zip"
        val downloadFileName = "$databaseName-${LocalDateTime.now().toString()
            .replace(":", "")
            .replace("-", "")}.zip"
        response.addHeader("Content-Disposition", "attachment; filename=${downloadFileName}")
        FileInputStream(zipFilePath).use { fileInputStream ->
            response.outputStream.use { outputStream ->
                fileInputStream.copyTo(outputStream)
            }
        }
    }

    private fun renderFile(
        tplName: String,
        targetPkg: String,
        tempDir: Path,
        view: Any
    ) {
        renderSimpleTemplate(tplName, File(tempDir.toFile(), targetPkg), view)
    }

    fun compressDirectoryToZip(sourceDirPath: String, zipFilePath: String) {
        val sourceDir = File(sourceDirPath)
        val zipFile = File(zipFilePath)

        FileOutputStream(zipFile).use { fileOutputStream ->
            ZipOutputStream(fileOutputStream).use { zipOut ->

                // 递归添加文件到 ZIP
                fun addToZip(prefix: String, file: File) {
                    val entryName = if (prefix.isEmpty()) file.name else "$prefix/${file.name}"
                    if (file.isDirectory) {
                        // 创建目录条目
                        zipOut.putNextEntry(ZipEntry("$entryName/"))
                        zipOut.closeEntry()
                        // 遍历子文件
                        file.listFiles()?.forEach { child ->
                            addToZip(entryName, child)
                        }
                    } else {
                        // 添加文件条目
                        FileInputStream(file).use { fis ->
                            zipOut.putNextEntry(ZipEntry(entryName))
                            val buffer = ByteArray(1024)
                            var length: Int
                            while (fis.read(buffer).also { length = it } > 0) {
                                zipOut.write(buffer, 0, length)
                            }
                            zipOut.closeEntry()
                        }
                    }
                }

                sourceDir.listFiles()?.forEach { file ->
                    addToZip("", file)
                }
            }
        }
    }
}